package com.list.model.specific;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.NodeIterator;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.ResIterator;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.ResourceFactory;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS;
import com.list.data.structure.ResourceWeight;
import com.list.data.structure.TripleWeights;
import com.list.data.structure.TripleWeightsList;
import com.list.data.structure.UncountedResources;
import com.list.model.abstracts.BaseModel;
import com.list.model.abstracts.Initializable;
import com.list.model.measure.Normalization;
import com.list.model.utils.FileAddress;
import com.list.model.utils.RelFinderLog;

public class MemoryInitializer implements Initializable{
	
	private BaseModel model;
    private UncountedResources ur;  
    private ResourceWeight weight;
    private Model jenamodel;
    private HashMap<Property, TripleWeightsList> weightTable;
    private HashMap<Property, Double> propertyWeightTable;
	
	public MemoryInitializer(BaseModel model) {
		super();
		this.model = model;
		this.jenamodel = model.getModel();
		this.weightTable = model.getTripleWeightList();
		this.propertyWeightTable = model.getPropertyWeightTable();
		ur = new UncountedResources();
		weight = new ResourceWeight();
	}
	
	@Override
	public void calculate() {
		initializePropertyWeights();
		initializeTripleWeights();
		updateWeightTableByPropertyWeight();
		initializeInstanceTripleTable();
//		write(FileAddress.WEIGHT_FILE_ADDRESS);
		RelFinderLog.log(RelFinderLog.DETAIL, "Initialization complete !!! \n");
	}

	private void initializeInstanceTripleTable() {
		for (Iterator<Property> iterator = weightTable.keySet().iterator(); iterator.hasNext();) {
			Property prop = iterator.next();
			TripleWeightsList tripleWeightsList = weightTable.get(prop);
			double value = 0.0;
			for (TripleWeights tripleWeights : tripleWeightsList) {
				value = tripleWeights.getWeight();
			}
			propertyWeightTable.put(prop, value);
		}
	}

	private void updateWeightTableByPropertyWeight() {
        Iterator<Property> e = this.weightTable.keySet().iterator();
        while (e.hasNext()) {
            Property key = e.next();
            double pweight = (1-this.getWeight(key));
            TripleWeightsList pws = this.weightTable.get(key);
            for (int i = 0; i < pws.size(); i++) {
                double tweight = pws.get(i).getWeight();
                double mergedweight = (pweight * 0.5) + (tweight * 0.5);
                pws.get(i).setWeight(mergedweight);
            }
        }
		
	}
	
    public double getWeight(Resource arc) {
        if (!this.weight.containsKey(arc)) {
            return 1.0;
        }
        double result = 0.0;
        result = this.weight.get(arc);
        if (result == 0.0) {
            return 1.0;
        } else {
            return result;
        }
    }

	private void initializeTripleWeights() {
		// TODO Auto-generated method stub
        ResIterator ri = jenamodel.listSubjectsWithProperty(RDF.type, OWL.ObjectProperty);
        Resource prop = null;
        while (ri.hasNext()) {
            prop = ri.next();
            Property property = ResourceFactory.createProperty(prop.getURI());

            TripleWeightsList pws = new TripleWeightsList();
            NodeIterator ni1 = jenamodel.listObjectsOfProperty(prop, RDFS.domain);

            while (ni1.hasNext()) {
                Resource domain = ni1.nextNode().asResource();

                NodeIterator ni2 = jenamodel.listObjectsOfProperty(prop, RDFS.range);
                while (ni2.hasNext()) {
                    Resource range = ni2.nextNode().asResource();
                    double w = this.getTripleWeightOfProperty(domain, property, range);
                    pws.add(new TripleWeights(domain, property, range, w, true));
                    pws.add(new TripleWeights(domain, property, range, w, false));
                }
            }
            weightTable.put(property, pws);
        }
        Normalization.normalizeByMinMax(weightTable);
	}

	private double getTripleWeightOfProperty(Resource domain,
			Property property, Resource range) {
		// TODO Auto-generated method stub
		
		
        double numerator = 0;
        double denominator = 0;

        ResIterator res1 = jenamodel.listResourcesWithProperty(RDF.type, domain);
        while (res1.hasNext()) {
            Resource dr = res1.nextResource();
            ResIterator res2 = jenamodel.listResourcesWithProperty(RDF.type, range);
            while (res2.hasNext()) {
                Resource rr = res2.nextResource();

                StmtIterator stmt = jenamodel.listStatements(dr, property, rr);
                StmtIterator stmt2 = jenamodel.listStatements(dr, null, rr);
                
                while (stmt.hasNext()) {
                    stmt.nextStatement();
                    numerator++;
                }
                while (stmt2.hasNext()) {
                    stmt2.nextStatement();
                    denominator++;
                }
            }
        }

        if (denominator == 0 || numerator == 0) {
            return 1.0;
        }

        double w = numerator / denominator;
        return 1.0 - w;
	}

	private void initializePropertyWeights() {
        List<Resource> empty = new ArrayList<Resource>();
        int countOfProperties = 0;
        ResourceWeight tempProperty = new ResourceWeight();
        ResIterator ri = model.getModel().listSubjectsWithProperty(RDF.type, OWL.ObjectProperty);
        while (ri.hasNext()) {
            Resource property = ri.next();
            ResIterator tripleOfProperty = model.getModel().listResourcesWithProperty(ResourceFactory.createProperty(property.getURI()));
            int count = this.count(tripleOfProperty);
            if (count == 0) {
                empty.add(property);
            } else {
                countOfProperties += count;
                tempProperty.put(property, (double) count);
            }
        }
        this.update(tempProperty, countOfProperties);
        this.updateEmptySet(empty);
	}
	
    private void updateEmptySet(@SuppressWarnings("UseOfObsoleteCollectionType") List<Resource> empty) {
        for (int i = 0; i < empty.size(); i++) {
            this.weight.put(empty.get(i), 1.0);
        }
    }

	private void update(ResourceWeight weights, int totalCount) {
		// TODO Auto-generated method stub
        for (int i = 0; i < this.ur.size(); i++) {
            Resource r = this.ur.get(i);
            if (weights.containsKey(r)) {
                double count = weights.get(r);
                totalCount -= count;
                weights.remove(ur.get(i));
            }
        }
        ResourceWeight temp = new ResourceWeight();
        for (Iterator<Resource> iterator = weights.keySet().iterator(); iterator.hasNext();) {
			Resource key =  iterator.next();
			double value = weights.get(key);
			temp.put(key, (value / totalCount));
		}
        
        this.weight.putAll(Normalization.normalizeByMinMax(temp));
		
	}

	private int count(ResIterator tripleOfProperty) {
        int result = 0;
        while (tripleOfProperty.hasNext()) {
            result++;
            tripleOfProperty.next();
        }
        return result;
	}
	
	private void write(String filename) {
        try {
            File file = new File(filename);
            file.createNewFile();
            FileOutputStream fos = new FileOutputStream(file);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            PrintWriter pw = new PrintWriter(bos);
            
            Iterator<Property> e = this.propertyWeightTable.keySet().iterator();
            while (e.hasNext()) {
				Property key = e.next();
				double pws = this.propertyWeightTable.get(key);
				System.out.println(key + "," + pws + "\n");
				pw.write(key + "," + pws);
				pw.println();
			}

            pw.flush();
            pw.close();
            bos.close();
            fos.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
	}
	
	
	

}
